#ifndef __DATA_INPUT_OUTPUT_H__
#define __DATA_INPUT_OUTPUT_H__

#include <DataUnit.h>
#include <CharDataUnit.h>
#include <TransformationProcess.h>
#include <iostream>
#include <fstream>

#define BIT_BUFFER_SIZE 1048576
#define IN_BUFFER_SIZE 1048576

//TAMAÑO DEL BUFFER PARA EL BLOCK SORTING :D
#define BS_BUFFER_SIZE 256*1024

using namespace std;
using namespace processes;
using namespace dataunits;
using namespace chardataunits;

namespace DataInputOutput {

//INTERFACES

class DataInput {
public:
	DataInput() {};
	virtual DataUnit* getNext() throw(EndOfDataException) =0;
	virtual ~DataInput() {};
};

class DataOutput {
public:
	DataOutput() {};
	virtual void append(DataUnit* dataUnit) =0;
	virtual ~DataOutput() {};
};

class BitDataInput {
public:
	BitDataInput() {};
	virtual bool getNextBit() throw(EndOfDataException)=0;
	virtual ifstream* getFile() =0;
	virtual ~BitDataInput() {};
};

class BitDataOutput {
public:
	BitDataOutput() {};
	virtual void writeBit(bool value) =0;
	virtual void flush() =0;
	virtual void complete()=0;
	virtual ~BitDataOutput() {};
};

//IMPLEMENTACIONES

class DecoratorDataInput : public DataInput{
public:
	DecoratorDataInput(SingleTransformationProcess* singleTransformationProcess,
			DataInput* dataInput);
	DataUnit* getNext() throw(EndOfDataException);
	virtual ~DecoratorDataInput();
private:
	SingleTransformationProcess* singleTransformationProcess;
	DataInput* dataInput;
};

class DecoratorDataOutput : public DataOutput {
public:
	DecoratorDataOutput(SingleTransformationProcess* singleTransformationProcess,
			DataOutput* dataOutput);
	void append(DataUnit* dataUnit);
	virtual ~DecoratorDataOutput();
private:
	SingleTransformationProcess* singleTransformationProcess;
	DataOutput* dataOutput;
};


class AbstractBufferedDecorator {
protected:
	BlockTransformationProcess* blockTransformationProcess;
	DataUnit** buffer;
	unsigned int current;
        unsigned int bufferSize;
        unsigned int initialBufferSize;
        unsigned int mallocBufferSize;
	void prepareBuffer() throw(EndOfDataException);
	virtual void workBuffer() throw(EndOfDataException) =0;
public:
	AbstractBufferedDecorator(BlockTransformationProcess* process, unsigned int bufferSize);
	virtual ~AbstractBufferedDecorator();
};


class BufferedDecoratorDataInput : public DataInput, public AbstractBufferedDecorator {
public:
	BufferedDecoratorDataInput(BlockTransformationProcess* btfp, DataInput* di);
	DataUnit* getNext() throw(EndOfDataException);
	void workBuffer() throw(EndOfDataException);
	virtual ~BufferedDecoratorDataInput();
private:
	DataInput* dataInput;
};

class BufferedDecoratorDataOutput : public DataOutput, public AbstractBufferedDecorator {
public:
	BufferedDecoratorDataOutput(BlockTransformationProcess* btp, DataOutput* datao);
	void append(DataUnit* dataUnit);
	void workBuffer() throw(EndOfDataException);
	virtual ~BufferedDecoratorDataOutput();
private:
	DataOutput* dataOutput;
};


class FileDataInput : public DataInput{
public:
	FileDataInput(const char* fileRoute);
	DataUnit* getNext() throw(EndOfDataException);
	virtual ~FileDataInput();
private:
	void reloadBuffer();
	ifstream file;
	unsigned int loaded;
	unsigned int count;
	char buffer[IN_BUFFER_SIZE];
};

class FileDataOutput : public DataOutput{
public:
	FileDataOutput(const char* fileRoute);
	void append(DataUnit* dataUnit);
	virtual ~FileDataOutput();
private:
	ofstream file;
};

//IMPLEMENTACIONES DEL FILE BIT (LECTURA/ESCRITURA DE BITS)

class FileBitDataInput : public BitDataInput {
public:
	FileBitDataInput(const char* fileName);
	bool getNextBit() throw(EndOfDataException);
	ifstream* getFile();
	virtual ~FileBitDataInput();
private:
	void reloadBuffer();
	ifstream* inputFile;
	char buffer[ BIT_BUFFER_SIZE ];
	unsigned int bitCount;
	unsigned int byteCount;
	unsigned int loadedCount;
};

class FileBitDataOutput: public BitDataOutput{
public:
	FileBitDataOutput(const char* fileName);
	void writeBit(bool value);
	virtual void flush();
	virtual void complete();
	virtual ~FileBitDataOutput();
private:
	ofstream* outputFile;
	char buffer[ BIT_BUFFER_SIZE ];
	unsigned int bitCount;
	unsigned int byteCount;
};

}
#endif //__DATA_INPUT_OUTPUT_H__


